package com.lagou.edu.factory;

import com.lagou.edu.annotation.support.DefaultAnnotationHandler;
import com.lagou.edu.utils.FileScanUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    // 存储对象
    private static Map<String, BeanDefinition> customBeanMap = new HashMap<>();
    // 按照接口存储对象
    private static Map<String, BeanDefinition> interfaceMap = new HashMap<>();

    static {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            // 处理注解
            parseAndScanAnnotation(rootElement);
            // 处理xmlbean
            //parseAndScanXmlBeans(rootElement);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void parseAndScanXmlBeans(Element rootElement) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException {
        // 处理beans
        List<Element> beanList = rootElement.selectNodes("//bean");
        for (int i = 0; i < beanList.size(); i++) {
            Element element = beanList.get(i);
            // 处理每个bean元素，获取到该元素的id 和 class 属性
            String id = element.attributeValue("id");
            String clazz = element.attributeValue("class");
            registryBean(id, clazz);
        }

    }

    private static void parseAndScanAnnotation(Element rootElement) {
        // 获取注解包根路径
        Element componentScanElement = (Element) rootElement.selectSingleNode("//component-scan");
        String basePackage = componentScanElement.attributeValue("base-package");
        //获取到根包所有的类名
        List<String> allClassNames = FileScanUtils.scanAllClassFiles(basePackage);
        // 类加载获取其对应的注解。
        DefaultAnnotationHandler defaultAnnotationHandler = new DefaultAnnotationHandler();
        // 初始化基本对象
        defaultAnnotationHandler.processAnnotations(allClassNames);
        // 解决对象之间的引用依赖
        defaultAnnotationHandler.doProcessPropertiesBeanAutowired();
    }

    /**
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */
    public static Object getBean(String id) {
        BeanDefinition beanDefinition = customBeanMap.get(id);
        if (beanDefinition != null) {
            return beanDefinition.getProxy();
        }
        return beanDefinition;
    }

    /**
     * 根据类名称获取：接口类或者实现类
     *
     * @param clazz
     * @return
     */
    public static Object getBean(Class<?> clazz) {
        if (clazz != null) {
            BeanDefinition tryGetOne = customBeanMap.get(clazz.getName());
            if (tryGetOne != null) {
                return tryGetOne.getProxy();
            }
            BeanDefinition beanDefinition = interfaceMap.get(clazz.getName());
            if (beanDefinition != null) {
                return beanDefinition.getProxy();
            }
        }
        return null;
    }
    //
    public static BeanDefinition registryBeanByInterface(String name, Class<?> clazz) {
        // 实例化之后的对象
        try {
            //通过类构造函数实例化对象
            Object bean = clazz.getDeclaredConstructor().newInstance();
            //根据对象获取代理对象
            Object proxy = getProxy(bean);
            // 存储到map中待用  根据类名注入beanDefinition
            BeanDefinition beanDefinition = new BeanDefinition(bean, proxy);
            registryBeanByInterface(name, beanDefinition);
            return beanDefinition;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void registryBeanByInterface(String name, BeanDefinition bean) {
        interfaceMap.put(name, bean);
    }

    public static void registryBean(String id, BeanDefinition bean) {
        customBeanMap.put(id, bean);
    }

    public static void registryBean(String id, String clazz) throws ClassNotFoundException {
        registryBean(id, Class.forName(clazz));
    }

    public static void registryBean(String id, Class<?> clazz) {
        try {
            // 实例化之后的对象
            Object bean = clazz.getDeclaredConstructor().newInstance();
            Object proxy = getProxy(bean);
            // 把bean定义存储到map中待用
            BeanDefinition beanDefinition = new BeanDefinition(bean, proxy);
            customBeanMap.put(id, beanDefinition);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Object getProxy(Object bean) {
        ProxyFactory proxyFactory = new ProxyFactory();
        Object proxy = null;
        //如果类实现了接口就用JDK动态代理获取代理对象否则就用CGlib获取代理对象（对加有事务注解的方法拦截注入了事务的相关方法）
        if (bean.getClass().getInterfaces().length > 0) {
            proxy = proxyFactory.getJdkProxy(bean);
        } else {
            proxy = proxyFactory.getCglibProxy(bean);
        }
        return proxy;
    }
}
